/*
 容量(NetCapacity)项目 @author yanghongdong
*/
(function (global, factory) {
  'use strict';

  (typeof exports === 'object' && typeof module !== 'undefined') ? factory(exports):
    (typeof define === 'function' && define.amd) ? define(['exports'], factory) :
    factory((global.N = {}));
}(this, function (exports) {
  'use strict';

  var version = "1.0.0";

  /*
   * @namespace Util
   *
   * Various utility functions, used by NetCapacity internally.
   */

  var freeze = Object.freeze;
  Object.freeze = function (obj) {
    return obj;
  };

  // @function extend(dest: Object, src?: Object): Object
  // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
  function extend(dest) {
    var i, j, len, src;

    for (j = 1, len = arguments.length; j < len; j++) {
      src = arguments[j];
      for (i in src) {
        dest[i] = src[i];
      }
    }
    return dest;
  }

  // @function create(proto: Object, properties?: Object): Object
  // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
  var create = Object.create || (function () {
    function F() {}
    return function (proto) {
      F.prototype = proto;
      return new F();
    };
  })();

  // @function bind(fn: Function, …): Function
  // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
  // Has a `L.bind()` shortcut.
  function bind(fn, obj) {
    var slice = Array.prototype.slice;

    if (fn.bind) {
      return fn.bind.apply(fn, slice.call(arguments, 1));
    }

    var args = slice.call(arguments, 2);

    return function () {
      return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
    };
  }

  // @function trim(str: String): String
  // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
  function trim(str) {
    return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
  }

  // @function rtrim(str: String, mask: String): String
  function rtrim(str, mask) {
    if (str && mask) {
      return str.replace(new RegExp('\\' + mask + '+$', 'g'), '');
    }

    return str.replace(/\s+$/g, '');
  }

  // @function splitWords(str: String): String[]
  // Trims and splits the string on whitespace and returns the array of parts.
  function splitWords(str) {
    return trim(str).split(/\s+/);
  }

  // @function isArray(obj): Boolean
  // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
  var isArray = Array.isArray || function (obj) {
    return (Object.prototype.toString.call(obj) === '[object Array]');
  };

  var isObject = Object.isObject || function (obj) {
    return (Object.prototype.toString.call(obj) === '[object Object]');
  };

  // @function indexOf(array: Array, el: Object): Number
  // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
  function indexOf(array, el) {
    for (var i = 0; i < array.length; i++) {
      if (array[i] === el) {
        return i;
      }
    }
    return -1;
  }

  function checkTwoArr(arr1, arr2) {
    if (!isArray(arr1) || !isArray(arr2)) {
      return false;
    } else if (arr1.length !== arr2.length) {
      return false;
    }

    return true;
  }

  // 对数字为整数的数组取整
  function floorArr(a) {
    if (!isArray(a)) {
      return false;
    } else if (a.length === 0) {
      return a;
    } else {
      const b = [],
        length = a.length;

      for (let i = 0; i < length; i++) {
        b.push(Math.floor(a[i]));
      }

      return b;
    }
  }

  // @function getMaxOfArray(array): Number
  // (https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/max)
  function getMaxOfArray(numArray) {
    return Math.max.apply(null, numArray);
  }

  // @function getMinOfArray(array): Number
  // (https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/max)
  function getMinOfArray(numArray) {
    return Math.min.apply(null, numArray);
  }

  function getRandomLatLng(map) {
    var bounds = map.getBounds(),
      southWest = bounds.getSouthWest(),
      northEast = bounds.getNorthEast(),
      lngSpan = northEast.lng - southWest.lng,
      latSpan = northEast.lat - southWest.lat;

    return new L.LatLng(
      southWest.lat + latSpan * Math.random(),
      southWest.lng + lngSpan * Math.random());
  }

  function randomPopulate(map, num, latlng, layerGroup) {
    for (var i = 0; i < num; i++) {
      var latlng = this.getRandomLatLng(map),
        marker = new L.Marker(latlng);

      marker.tmp = i;

      marker.on('click', function (e) {
        console.log(e.latlng);

        L.popup({
            offset: [0, -20]
          })
          .setLatLng(e.latlng)
          .setContent('人员' + e.target.tmp + '信息')
          .openOn(map);
      });

      layerGroup.addLayer(marker);
    }

    return false;
  }

  // function populate(map, layerGroup, points, avatarPath) {
  function populate(obj) {
    if (!(obj.points instanceof Array)) {
      throw new Error('praram type error 第三个参数类型错误');
    }

    var markerTypeList = ['workerInfo', 'facilityInfo', 'equipmentInfo'];

    var length = obj.points.length,
      markerType = obj.markerType,
      icon = false;

    if (length === 0) {
      return false;
    }

    if (markerType !== false && $.inArray(markerType, markerTypeList) !== -1) {
      switch (markerType) {
        case 'workerInfo':
          var iconImage = 'flat-avatar.png';

          break;
        case 'facilityInfo':
          var iconImage = 'fac.png';

          break;
        case 'equipmentInfo':
          var iconImage = 'device.png';

          break;
        default:
          var iconImage = false;
      }

      icon = iconImage !== false ? L.icon({
        // iconUrl: avatarPath + points[i].avatar,
        iconUrl: obj.avatarPath + iconImage,
        iconSize: [40, 40],
        className: 'avatar-marker',
        // popupAnchor: [-3, -76],
        popupAnchor: [0, 0]
      }) : false;
    }

    var markerIcon = (icon !== false) ? {
      icon: icon
    } : {};

    for (var i = 0; i < length; i++) {
      var marker = new L.Marker({
        lat: obj.points[i].lat,
        lng: obj.points[i].lng
      }, markerIcon);

      marker.pid = obj.points[i].id;
      marker.markerType = obj.markerType;

      obj.layerGroup.addLayer(marker);
    }

    return true;
  }

  function getTwoDimArr(points) {
    if (!(points instanceof Array)) {
      throw new Error('praram type error 参数类型错误');
    }

    var length = points.length;

    if (length === 0) {
      return false;
    }

    var twoDimArr = [];

    for (var i = 0; i < length; i++) {
      var arr = [points[i].lat, points[i].lng];

      twoDimArr.push(arr);
    }

    return twoDimArr;
  }

  function getXHRReadyStateInfo(code) {
    var sCode = parseInt(code, 10);

    if (typeof sCode !== 'number' || $.inArray(sCode, [0, 1, 2, 3, 4]) === -1) {
      return false;
    }

    var info = {
      0: '未初始化 -- 尚未调用.open()方法',
      1: '启动 -- 已经调用.open()方法，但尚未调用.send()方法',
      2: '发送 -- 已经调用.send()方法，但尚未接收到响应',
      3: '接收 -- 已经接收到部分响应数据',
      4: '接收 -- 已经接收到部分响应数据'
    };

    return info[code];
  }

  // 生成legend dom
  function getColorLegend(colors) {
    if (!isArray(colors)) {
      throw new Error('param type error 参数不是数组');
    }

    var length = colors.length,
      labels = '';

    if (length > 0) {
      for (var i = 0; i < length; i++) {
        labels += '<div><i style="background:' + colors[i][0] + '"></i><span>' +
          colors[i][1] + '</span></div>';
      }
    }

    return labels;
  }


  function getCenter(points) {
    if (!(points instanceof Array)) {
      throw new Error('参数类型错误 param type error');
    }

    var length = points.length;

    if (length === 0) {
      // 传递的参数（数组）长度为0
      return false;
    } else {
      var sPoints = points;

      if (isArray(points[0])) {
        var sPoints = [];

        // 如果points[0]为数组 [100.102, 20.102], 则将其适配为{x: 100.100, y: 20.100}形式
        for (var j = 0; j < length; j++) {
          sPoints.push({
            x: points[j][0],
            y: points[j][1]
          });
        }
      } else if (isObject(points[0]) && points[0].lat && points[0].lng) {
        // {lat: 100.102, lng: 20.102}形式
        var sPoints = [];

        for (var j = 0; j < length; j++) {
          sPoints.push({
            x: points[j].lng,
            y: points[j].lat
          });
        }
      } else if (isObject(points[0]) && points[0].lat && points[0].lon) {
        // {lat: 20.102, lng: 100.102}形式
        var sPoints = [];

        for (var j = 0; j < length; j++) {
          sPoints.push({
            x: points[j].lon,
            y: points[j].lat
          });
        }
      }

      var maxY = sPoints[0].y,
        maxX = sPoints[0].x,
        minY = sPoints[0].y,
        minX = sPoints[0].x;

      for (var i = 1, point; point = sPoints[i]; i++) {
        // 找出最大、最小经纬度
        if (maxY < point.y) {
          maxY = point.y;
        }
        if (maxX < point.x) {
          maxX = point.x;
        }
        if (minY > point.y) {
          minY = point.y;
        }
        if (minX > point.x) {
          minX = point.x;
        }
      }

      // 返回中心点经纬度
      return {
        lat: (maxY + minY) / 2,
        lng: (maxX + minX) / 2
      };
    }
  }

  var Util = (Object.freeze || Object)({
    freeze: freeze,
    extend: extend,
    create: create,
    bind: bind,
    trim: trim,
    rtrim: rtrim,
    splitWords: splitWords,
    isArray: isArray,
    isObject: isObject,
    indexOf: indexOf,
    populate: populate,
    randomPopulate: randomPopulate,
    getTwoDimArr: getTwoDimArr,
    getRandomLatLng: getRandomLatLng,
    getXHRReadyStateInfo: getXHRReadyStateInfo,
    getColorLegend: getColorLegend,
    checkTwoArr: checkTwoArr,
    floorArr: floorArr,
    getMaxOfArray: getMaxOfArray,
    getMinOfArray: getMinOfArray,
    getCenter: getCenter
  });


  // @class Class
  // @aka N.Class

  // @section
  // @uninheritable

  // Thanks to John Resig and Dean Edwards for inspiration!

  function Class() {}

  Class.extend = function (props) {

    // @function extend(props: Object): Function
    // [Extends the current class](#class-inheritance) given the properties to be included.
    // Returns a Javascript function that is a class constructor (to be called with `new`).
    var NewClass = function () {

      // call the constructor
      if (this.initialize) {
        this.initialize.apply(this, arguments);
      }

      // call all constructor hooks
      this.callInitHooks();
    };

    var parentProto = NewClass.__super__ = this.prototype;

    var proto = create(parentProto);
    proto.constructor = NewClass;

    NewClass.prototype = proto;

    // inherit parent's statics
    for (var i in this) {
      if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
        NewClass[i] = this[i];
      }
    }

    // mix static properties into the class
    if (props.statics) {
      extend(NewClass, props.statics);
      delete props.statics;
    }

    // mix includes into the prototype
    if (props.includes) {
      checkDeprecatedMixinEvents(props.includes);
      extend.apply(null, [proto].concat(props.includes));
      delete props.includes;
    }

    // merge options
    if (proto.options) {
      props.options = extend(create(proto.options), props.options);
    }

    // mix given properties into the prototype
    extend(proto, props);

    proto._initHooks = [];

    // add method for calling all hooks
    proto.callInitHooks = function () {

      if (this._initHooksCalled) {
        return;
      }

      if (parentProto.callInitHooks) {
        parentProto.callInitHooks.call(this);
      }

      this._initHooksCalled = true;

      for (var i = 0, len = proto._initHooks.length; i < len; i++) {
        proto._initHooks[i].call(this);
      }
    };

    return NewClass;
  };


  // @function include(properties: Object): this
  // [Includes a mixin](#class-includes) into the current class.
  Class.include = function (props) {
    extend(this.prototype, props);
    return this;
  };

  // @function mergeOptions(options: Object): this
  // [Merges `options`](#class-options) into the defaults of the class.
  Class.mergeOptions = function (options) {
    extend(this.prototype.options, options);
    return this;
  };

  // @function addInitHook(fn: Function): this
  // Adds a [constructor hook](#class-constructor-hooks) to the class.
  Class.addInitHook = function (fn) { // (Function) || (String, args...)
    var args = Array.prototype.slice.call(arguments, 1);

    var init = typeof fn === 'function' ? fn : function () {
      this[fn].apply(this, args);
    };

    this.prototype._initHooks = this.prototype._initHooks || [];
    this.prototype._initHooks.push(init);
    return this;
  };

  function checkDeprecatedMixinEvents(includes) {
    if (!L || !L.Mixin) {
      return;
    }

    includes = isArray(includes) ? includes : [includes];

    for (var i = 0; i < includes.length; i++) {
      if (includes[i] === L.Mixin.Events) {
        console.warn('Deprecated include of L.Mixin.Events: ' +
          'this property will be removed in future releases, ' +
          'please inherit from L.Evented instead.', new Error().stack);
      }
    }
  }

  function getTile(vendor, type) {
    switch (vendor) {
      case 'tian':
        switch (type) {
          case 'normal':
            return L.layerGroup([L.tileLayer.chinaProvider('TianDiTu.Normal.Map', {
              maxZoom: 18,
              minZoom: 1
            }), L.tileLayer.chinaProvider('TianDiTu.Normal.Annotion', {
              maxZoom: 18,
              minZoom: 1
            })]);
          case 'satellite':
            return L.layerGroup([L.tileLayer.chinaProvider('TianDiTu.Satellite.Map', {
              maxZoom: 18,
              minZoom: 1
            }), L.tileLayer.chinaProvider('TianDiTu.Satellite.Annotion', {
              maxZoom: 18,
              minZoom: 1
            })]);
          case 'terrain':
            return L.layerGroup([L.tileLayer.chinaProvider('TianDiTu.Terrain.Map', {
              maxZoom: 18,
              minZoom: 1
            }), L.tileLayer.chinaProvider('TianDiTu.Terrain.Annotion', {
              maxZoom: 18,
              minZoom: 1
            })]);
        }
        break;
      default:
    }
  };

  function Map(mapId, defaultLayer, crs) {
    'use strict';

    var crs = crs || L.CRS.EPSG3857,
      defaultLayer = defaultLayer || TianMap.getNormal();

    var map = L.map(mapId, { // 地图
      crs: crs, // web墨卡托
      // crs: L.CRS.EPSG4326,
      doubleClickZoom: false,
      layers: [defaultLayer],
      zoomControl: false,
      attributionControl: false
    });

    this.map = map;
    this.defaultLayer = defaultLayer;

    // // 设置工具
    // this.addZoom();
    // this.addMeasure();
    // this.addCoord();
    // this.addScale();

    return this;
  }

  Map.prototype = {
    getLmap: function () {
      return this.map;
    },
    setCView: function (center, zoom) {
      this.map.setView(center, zoom);

      return this;
    },
    setLayers: function (base, over, option) {
      var defaultLayer = this.defaultLayer;

      var base = base || { // 切换基础图层的对象
          "地图": defaultLayer,
          "影像": getTile('tian', 'satellite'),
          "地形": getTile('tian', 'terrain')
        },
        over = over || {},
        option = option || {
          position: 'topright',
          collapsed: false
        };

      var layerControl = L.control.layers(base, over, option).addTo(this.map);

      for (var p in over) {
        this.map.addLayer(over[p]);
      }

      return layerControl;
    },
    // addMeasure: function() {
    //     // 测距离和面积
    //     L.control.measure({
    //         position: 'topleft',
    //         primaryLengthUnit: 'meters',
    //         primaryAreaUnit: 'sqmeters',
    //         // activeColor: '#ABE67E',
    //         activeColor: '#ff0000',
    //         // completedColor: '#C8F2BE',
    //         completedColor: '#ff0000',
    //         localization: 'cn',
    //         popupOptions: { className: 'leaflet-measure-resultpopup', autoPanPadding: [10, 10] }
    //     }).addTo(this.map);

    //     return this;
    // },
    addZoom: function () {
      // 缩放
      L.control.zoom({
        zoomInTitle: '放大',
        zoomOutTitle: '缩小'
      }).addTo(this.map);

      return this;
    },
    addCoord: function () {
      // 经纬度坐标
      L.control.coordinates({
        position: "bottomleft",
        labelTemplateLat: "纬度: {y}",
        labelTemplateLng: "经度: {x}",
        enableUserInput: false
      }).addTo(this.map);

      return this;
    },
    addScale: function () {
      L.control.scale({
        imperial: false
      }).addTo(this.map);

      return this;
    },
    // addReturn: function() {
    //     var map = this.map;

    //     // 地图置中
    //     L.easyButton('<span class="fa fa-undo"></span>', function(e) {
    //         map.setView(e._map._lastCenter, 12);
    //     }, '返回').addTo(map);

    //     return this;
    // },
    // addSlider: function(callee) {
    //     L.control.slider(callee, {
    //         position: 'topleft',
    //         max: 1,
    //         value: 1,
    //         step: 0.1,
    //         size: '100px',
    //         // orientation: 'vertical',
    //         orientation: 'horizontal',
    //         id: 'slider',
    //         title: '透明度',
    //         logo: 'S'
    //     }).addTo(this.map);

    //     return this;
    // }
  };

  var createMap = function (id, defaultLayer, crs) {
    return new Map(id, defaultLayer, crs);
  }

  // misc
  var oldN = window.N;

  function noConflict() {
    window.N = oldN;

    return this;
  }

  window.N = exports;

  exports.version = version;
  exports.noConflict = noConflict;
  exports.Util = Util;
  exports.Class = Class;
  exports.getTile = getTile;
  exports.Map = Map;
  exports.map = createMap;

}));